//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name: wbtcnctn.cpp

Abstract:

Notes:
--*/

#include <windows.h>
#include "wbtcnctn.h"
#include "wbtshlib.h"

#ifdef DEBUG
#include <debug.h>
#endif


#define CONNECTION_DEFAULT                1
#define CONNECTION_AUTOCONNECT            2

#define NUM_CONNECT_REG_SZ_VALUES         8
#define CONNECT_REG_SZ_DESCRIPTION        0
#define CONNECT_REG_SZ_STARTEXE           1
#define CONNECT_REG_SZ_START_CMDLINE      2
#define CONNECT_REG_SZ_PROP_EXE           3
#define CONNECT_REG_SZ_PROP_CMDLINE       4
#define CONNECT_REG_SZ_DELETE_EXE         5
#define CONNECT_REG_SZ_DELETE_CMDLINE     6
#define CONNECT_REG_DWORD_HIDE_CONNECTION 7


#define CONNECTION_DEFAULT_TYPE           0
#define CONNECTION_DEFAULT_KEY            1
#define ENABLE_AUTO_CONNECT               2


const TCHAR szDefaultConnectRegValue[][22] = 
      {
      TEXT("ConnectionDefaultType"),
      TEXT("ConnectionDefaultKey"),
      TEXT("EnableAutoConnect")
      };

const TCHAR szConnectionRegValue[][22]=
      {
      TEXT("Description"),
      TEXT("StartExe"),
      TEXT("StartCmdLineArgs"),
      TEXT("PropertiesExe"),
      TEXT("PropertiesCmdLineArgs"),
      TEXT("DeleteExe"),
      TEXT("DeleteCmdLineArgs"),
	  TEXT("HideConnection")
      };

//const TCHAR *pszTSCClassName = TEXT("UIMainClass");	//the window class name of the terminal server client
const TCHAR *pszTSCClassName = TEXT("TSSHELLWND");	//the window class name of the terminal server client
const TCHAR *pszWebCClassName = TEXT("iExplore");		//the window class name of the web client

typedef struct tagCCdata {
   
   PCONNECT_DATA cdata; 
   PCLIENT_DATA cldata;

} CCDATA, *PCCDATA;

/*++

--*/


CConnectList::CConnectList() {
      head = NULL;
      tail = NULL;
}


/*++

--*/


CConnectList::~CConnectList() {
   
   DeleteAllConnections();

}

/*++

--*/


BOOL
CConnectList::InitInstance(BOOL bRestart) {

   CClientList::InitInstance();   
   this->bRestart = bRestart;
   LoadConnections();
   ClearStartupFlags();
   EnumConnections();
   GetStartupInfo();

   return TRUE;
}


/*++

--*/

void
CConnectList::ClearStartupFlags() {

   INT count = 0;
   PCONNECT_ENTRY ptr = head;
      
   while (ptr) {
      ptr->dwStartupFlag = 0x00000000;
      count++;
      ptr = ptr->next;
      }
}


/*++

--*/

void
CConnectList::EnumConnections() {

   INT count = 0;
   PCONNECT_ENTRY ptr = head;
      
   while (ptr) {
      ptr->index = count;
      count++;
      ptr = ptr->next;
      }

}


/*++

--*/


BOOL
CConnectList::GetConnectionDataByIndex(INT index, PCONNECT_DATA &pcdata) {
   
   PCONNECT_ENTRY ptr;
   
   ptr = head;
   while (ptr) {

      if (ptr->index == index) {
         pcdata = ptr->cdata;
         return TRUE;
         }

      ptr = ptr->next;
   }

   DEBUGMSG(ZONE_ERROR,
           (TEXT("ERROR: GetConnectionDataByIndex(), index %d does not exist!\r\n"),
            index));

   pcdata = NULL;
   return FALSE;
}



/*++

--*/

DWORD GetWFIcaClientPID(DWORD rootPID)
{
	bool looking4nextPID = false;
	DWORD pid = 0;

	PROCESSENTRY32 pe;
	HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

	if(hSnapShot == (HANDLE)-1)
	{
		return pid;
	}

	memset(&pe,0,sizeof(PROCESSENTRY32));
	pe.dwSize = sizeof(PROCESSENTRY32);

	if(Process32First(hSnapShot, &pe))
	{
		while(TRUE)
		{
			if ( looking4nextPID )
			{
				if (
					0 == _tcscmp(pe.szExeFile, _T("WFICACE.exe")) 
				   )
				{
					pid = pe.th32ProcessID;
					break;
				}
			}
			else if ( pe.th32ProcessID == rootPID )
			{
				looking4nextPID = true;
			}

			if(!Process32Next(hSnapShot, &pe))
			{				
				break;
			}
		}
	}

	CloseToolhelp32Snapshot(hSnapShot);
	return pid;
}

BOOL
CConnectList::FindConnectionHwnd(INT index) 
{
   DWORD orgPID = 0;       
   PCONNECT_DATA cdata = NULL;
   PCLIENT_DATA cldata = NULL;   
   CCDATA ccdata;
   INT client;

   if (!GetConnectionDataByIndex(index, cdata)) {
      DEBUGMSG(ZONE_ERROR,(TEXT("ERROR: Internal error in FindConnectionHwnd()\r\n")));
      return FALSE;
      }
   
   client = GetClientIndexByRegKey(cdata->lpszClientRegistrationKey);
   GetClientDataByIndex(client, cldata);

   ccdata.cdata  = cdata;
   ccdata.cldata = cldata;

   orgPID = cdata->pi.dwProcessId;

   if (0 == _tcscmp(cdata->lpszType, _T("ICA")))
   {
	   cdata->pi.dwProcessId = GetWFIcaClientPID(cdata->pi.dwProcessId);
   }

   EnumWindows(FindWindowProc, (LPARAM)&ccdata);

   cdata->pi.dwProcessId = orgPID;

   if (cdata->hwnd) {
      return TRUE;
   }
   return FALSE;
}

/*++

--*/

BOOL CALLBACK
CConnectList::FindWindowProc(HWND hwnd, LPARAM lParam) {
      
   DWORD dwProcessId;
   PCCDATA pccdata = (PCCDATA)lParam;   
   PCONNECT_DATA cdata = pccdata->cdata; 
   PCLIENT_DATA cldata = pccdata->cldata;
   const TCHAR *pszClientClass = (cldata->clientClass == WEB_CLIENT) ? pszWebCClassName : pszTSCClassName;
   TCHAR szClass[16];
   GetWindowThreadProcessId(hwnd, &dwProcessId);
   GetClassName(hwnd, szClass, 15);
   if ((cdata->pi.dwProcessId == dwProcessId) && (IsWindowVisible(hwnd)) && 
	   ( (0 == _tcsncmp(szClass, pszClientClass, 15)) || 
	     ( (0 == _tcscmp(szClass, _T("Dialog"))) && (0 == _tcscmp(cdata->lpszConnectionKey, _T("Default"))) && (cldata->clientClass == TERMINAL_SERVER_CLIENT)) ||
	     ( (0 == _tcscmp(szClass, _T("WFIcaClient"))) && (cldata->clientClass == TERMINAL_SERVER_CLIENT)) ||
		 ( (0 == _tcscmp(szClass, _T("PuTTY"))) && (cldata->clientClass == TERMINAL_SERVER_CLIENT)) ||
		 ( (0 == _tcsncmp(szClass, _T("ATL:"),4)) && (cldata->clientClass == TERMINAL_SERVER_CLIENT))
	   ) 
	  )
   {      
      cdata->hwnd = hwnd;
      return FALSE;
   }

   cdata->hwnd = NULL;
   return TRUE;
}

/*++

--*/


INT
CConnectList::GetConnectionCount() {
   
   INT count = 0;
   PCONNECT_ENTRY ptr = head;
      
   while (ptr) {
      count++;
      ptr = ptr->next;
      }
   return count;
}


/*++

--*/

DWORD
CConnectList::GetConnectionStartupInfo(INT index) {
 
   PCONNECT_ENTRY ptr;
   
   ptr = head;
   while (ptr) {

      if (ptr->index == index) {
         return ptr->dwStartupFlag;
         }

      ptr = ptr->next;
   }

   DEBUGMSG(ZONE_ERROR,
           (TEXT("ERROR: Connection %d does not exist!\r\n"),
            index));

   return 0;
}



/*++

--*/

INT
CConnectList::GetConnectionIndexByConnKey(LPWSTR lpszConnKey) {
      
   PCONNECT_ENTRY ptr;
   
   ptr = head;
   while (ptr) {
      
      if (wcscmp(ptr->cdata->lpszConnectionKey, lpszConnKey) == 0) {
         return ptr->index;
         }

      ptr = ptr->next;
   }
    
   return -1;
}


/*++

--*/

INT
CConnectList::GetConnectionIndexByConnKeyAndClientKey(LPWSTR lpszConnKey, LPWSTR lpszClientKey) {
      
   PCONNECT_ENTRY ptr;
   
   ptr = head;
   while (ptr) {
      
      if ((wcscmp(ptr->cdata->lpszConnectionKey, lpszConnKey) == 0)&&
          (wcscmp(ptr->cdata->lpszClientRegistrationKey, lpszClientKey) == 0)) {
         return ptr->index;
         }

      ptr = ptr->next;
   }
    
   return -1;
}


/*++

--*/

INT
CConnectList::GetActiveSessionCount() {
      
   PCONNECT_ENTRY ptr;
   INT count;
   BOOL bResult;
   DWORD dwExitCode;
   HANDLE hProcess;

   count = 0;
   ptr = head;
   while (ptr) {
         if(!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ptr->cdata->pi.dwProcessId))) {
            goto RESET_SESSION_PI;
            }

         bResult = GetExitCodeProcess(hProcess, &dwExitCode);
         if ((bResult)&&(dwExitCode == STILL_ACTIVE)) {
            count++;
            goto GET_NEXT_SESSION; 
            }
         // close process handle to clean up process space  
	 if ((bResult) && (ptr->cdata->pi.hProcess)) {
	      CloseHandle(ptr->cdata->pi.hProcess);
	 } 	
RESET_SESSION_PI:
      ptr->cdata->hwnd = NULL;
      memset(&ptr->cdata->pi, 0, sizeof(PROCESS_INFORMATION));

GET_NEXT_SESSION:
      CloseHandle(hProcess);
      ptr = ptr->next;
   }
    
   return count;
}


/*++

--*/

INT
CConnectList::GetActiveSessionNext(INT iReference) {
   
   PCONNECT_ENTRY ptr;
   INT iRetVal = -1;


   if (iReference < 0) {
      return iRetVal;
      }
   
   if (!GetActiveSessionCount()) {
      return iRetVal;
      }

   
   // Get the ptr to the reference session.
   ptr = head;
   while (ptr) {
      if (ptr->index == iReference) {
         break;
         }
      ptr = ptr->next;
      }
   
   if (ptr) {
      ptr = ptr->next;
      }

   // Search for the next active session from the reference 
   while(ptr) {
      if (ptr->cdata->pi.dwProcessId) {
         return ptr->index;
         }
      ptr = ptr->next;
      }
         
   // Search from the beginning of the list to the reference
   ptr = head;
   while (ptr) {
      if (ptr->index == iReference) {
         if (ptr->cdata->pi.dwProcessId) return ptr->index;
         else return -1;
         }
      if (ptr->cdata->pi.dwProcessId) {
         return ptr->index;
         }
      ptr = ptr->next;
      }
   
   return -1;
}


/*++

--*/

INT
CConnectList::GetActiveSessionPrev(INT iReference) {

   PCONNECT_ENTRY ptr;
   INT iRetVal = -1;


   if (iReference < 0) {
      return iRetVal;
      }
   
   if (!GetActiveSessionCount()) {
      return iRetVal;
      }

   
   // Get the ptr to the reference session.
   ptr = tail;
   while (ptr) {
      if (ptr->index == iReference) {
         break;
         }
      ptr = ptr->prev;
      }
   
   if (ptr) {
      ptr = ptr->prev;
      }

   // Search for the next active session from the reference 
   while(ptr) {
      if (ptr->cdata->pi.dwProcessId) {
         return ptr->index;
         }
      ptr = ptr->prev;
      }
         
   // Search from the beginning of the list to the reference
   ptr = tail;
   while (ptr) {
      if (ptr->index == iReference) {
         if (ptr->cdata->pi.dwProcessId) return ptr->index;
         else return -1;
         }
      if (ptr->cdata->pi.dwProcessId) {
         return ptr->index;
         }
      ptr = ptr->prev;
      }
   
   return -1;


}


/*++

--*/


void
CConnectList::DeleteConnectionFromList(INT index) {
     
   PCONNECT_ENTRY ptr;
   
   ptr = head;
   while (ptr) {

      if (ptr->index == index) {
         DeleteConnectionFromList(ptr);
         return;
         }

      ptr = ptr->next;
   }

   DEBUGMSG(ZONE_ERROR,
           (TEXT("ERROR: Connection %d does not exist!\r\n"),
            index));
            
            
   return;
}


/*++

--*/


VOID
CConnectList::GetStartupInfo() {
   
   INT  index;

   ClearDefault();
   index = GetDefaultIndex();

   if ((index == -1) && (GetConnectionCount() > 0)) {
      SetDefault(0);
      WriteDefaultToReg(0, FALSE);
      }
   else SetDefault(index);

   if(QueryAutostartSetting())
      SetAutostart(TRUE);  
   else
      SetAutostart(FALSE);


}



/*++

--*/

VOID
CConnectList::ClearDefault() {

   PCONNECT_ENTRY ptr;
   ptr = head;
   while (ptr) {
      ptr->dwStartupFlag = 0x00000000;
      ptr = ptr->next;
      }
}

/*++

--*/

VOID
CConnectList::SetDefault(INT index) {

   PCONNECT_ENTRY ptr;   
   ptr = head;
   while (ptr) {
      
      ptr->dwStartupFlag = 0x00000000;
      if (ptr->index == index) {
         ptr->dwStartupFlag = ptr->dwStartupFlag | CONNECTION_DEFAULT;
         }

      ptr = ptr->next;
   }
}


/*++

--*/


INT
CConnectList::GetDefault() {
      
   PCONNECT_ENTRY ptr;

   ptr = head;
   while (ptr) {
      
      if (ptr->dwStartupFlag) {
         return ptr->index;
         }

      ptr = ptr->next;
   }
   return -1;
}


/*++

--*/

VOID
CConnectList::SetAutostart(BOOL autostart) {

   PCONNECT_ENTRY ptr;   
   ptr = head;
   while (ptr) {
      
      if (ptr->dwStartupFlag) {
         if (autostart) 
            ptr->dwStartupFlag = ptr->dwStartupFlag | CONNECTION_AUTOCONNECT;
         else
            ptr->dwStartupFlag &= ~CONNECTION_AUTOCONNECT;
         return;
         }

      ptr = ptr->next;
   }
}


/*++

--*/

BOOL
CConnectList::IsDefaultAutostart(INT index) {

   PCONNECT_ENTRY ptr;
   BOOL result = FALSE;
   
   ptr = head;
   while (ptr) {

      if (ptr->index == index) {
         
         if (ptr->dwStartupFlag & CONNECTION_AUTOCONNECT) {
            return TRUE;
            }
         return FALSE;
         }

      ptr = ptr->next;
   }

   return FALSE;

}

/*++

--*/

BOOL
CConnectList::WriteDefault(INT index, BOOL autostart) {
   
   BOOL bResult;
   
   bResult = WriteDefaultToReg(index, autostart);
   GetStartupInfo();
   return bResult;
}



/*++

   Note: Combine writing default and autostart info into a single call since RegFlushKey
         can take some time depending on the OEM implementation.

--*/

BOOL
CConnectList::WriteDefaultToReg(INT index, BOOL autostart) {
         
   LONG  lResult;
   HKEY  hKey;
   DWORD dwAutostart;
   PCONNECT_DATA cdata;   
   HWND hwndTaskman = FindWindow(szTaskmanWndClassName, NULL);
   HWND hwndDesktop = FindWindow(szDesktopWndClassName, NULL);

   if ((GetConnectionDataByIndex(index, cdata) == -1) || (!cdata)) {
      return FALSE;
   }
      
   RETAILMSG(1,(TEXT("Setting default connection:\r\n")));
   RETAILMSG(1,(TEXT("  ClientKey: \"%s\"\r\n"), cdata->lpszClientRegistrationKey));
   RETAILMSG(1,(TEXT("  ConnectKey: \"%s\"\r\n"), cdata->lpszConnectionKey));

   lResult = RegOpenKeyEx(
             HKEY_LOCAL_MACHINE,
             szRegKeyTaskmanSettings,
             NULL,
             KEY_WRITE,
             &hKey
             );   
      
   if (lResult != ERROR_SUCCESS) {
      RETAILMSG(1,(TEXT("ERROR: Cannot open \"HKEY_LOCAL_MACHINE\\%s\".\r\n"), szRegKeyTaskmanSettings));
      return FALSE;
      }
   
   lResult = RegSetValueEx(
             hKey,
             szDefaultConnectRegValue[CONNECTION_DEFAULT_TYPE],
             0,
             REG_SZ,
             (PBYTE)cdata->lpszClientRegistrationKey,
             GET_WSTR_BYTES(cdata->lpszClientRegistrationKey)
             );

   if (lResult != ERROR_SUCCESS) {
      RETAILMSG(1,(TEXT("ERROR: Cannot write to \"HKEY_LOCAL_MACHINE\\%s\".\r\n"), szRegKeyTaskmanSettings));
      RegCloseKey(hKey);
      return FALSE;
      }
   

   lResult = RegSetValueEx(
             hKey,
             szDefaultConnectRegValue[CONNECTION_DEFAULT_KEY],
             0,
             REG_SZ,
             (PBYTE)cdata->lpszConnectionKey,
             GET_WSTR_BYTES(cdata->lpszConnectionKey)
             );

   if (lResult != ERROR_SUCCESS) {
      RETAILMSG(1,(TEXT("ERROR: Cannot write to \"HKEY_LOCAL_MACHINE\\%s\".\r\n"), szRegKeyTaskmanSettings));
      RegCloseKey(hKey);
      return FALSE;
      }
   
   dwAutostart = (DWORD)autostart;
   lResult = RegSetValueEx(
             hKey,
             szDefaultConnectRegValue[ENABLE_AUTO_CONNECT],
             0,
             REG_DWORD,
             (PBYTE)&dwAutostart,
             sizeof(DWORD)
             );

   if (lResult != ERROR_SUCCESS) {
      RETAILMSG(1,(TEXT("ERROR: Cannot write to \"HKEY_LOCAL_MACHINE\\%s\".\r\n"), szRegKeyTaskmanSettings));
      RegCloseKey(hKey);
      return FALSE;
      }
   
   SendMessage(hwndDesktop, WBTSHL_SETCURSOR, 0, CURSOR_WAIT);
   SendMessage(hwndTaskman, WBTSHL_SETCURSOR, 0, CURSOR_WAIT);
   lResult = RegFlushKey(hKey);
   SendMessage(hwndTaskman, WBTSHL_SETCURSOR, 0, CURSOR_ARROW);
   SendMessage(hwndDesktop, WBTSHL_SETCURSOR, 0, CURSOR_ARROW);

   if (lResult != ERROR_SUCCESS) {
      RETAILMSG(1,(TEXT("ERROR: RegFlushKey failed! (%d)\r\n"), lResult));
      RegCloseKey(hKey);
      return FALSE;
   }

   RegCloseKey(hKey);
   return TRUE;

}



/*++

--*/


INT
CConnectList::GetDefaultIndex() {

   TCHAR szClientKey[MAX_PATH];
   TCHAR szConnectKey[MAX_PATH];
   PCONNECT_ENTRY ptr;

   if (!QueryDefaultSettings(szClientKey, szConnectKey)) {
      return -1;
   }
   DEBUGMSG(ZONE_TRACE,(TEXT("Default connection type: \"%s\"\r\n"), szClientKey));
   DEBUGMSG(ZONE_TRACE,(TEXT("Default connection key : \"%s\"\r\n"), szConnectKey));


   ptr = head;
   while (ptr) {
     
      if ((wcscmp(ptr->cdata->lpszClientRegistrationKey, szClientKey) == 0)&&
          (wcscmp(ptr->cdata->lpszConnectionKey, szConnectKey) == 0)){
           return ptr->index;
          }
      ptr = ptr->next;
   }
   return -1;

}

/*++

--*/

BOOL
CConnectList::QueryAutostartSetting() {

   LONG  lResult;
   HKEY  hKey;
   DWORD dwAutostart;
   DWORD dwSize;
   DWORD dwType;

   lResult = RegOpenKeyEx(
             HKEY_LOCAL_MACHINE,
             szRegKeyTaskmanSettings,
             NULL,
             KEY_READ,
             &hKey
             ); 

   if (lResult != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_ERROR, 
              (TEXT("ERROR: Cannot open \"HKEY_LOCAL_MACHINE\\%s\".\r\n"), szRegKeyTaskmanSettings));
      return FALSE;
      }

   dwSize  = sizeof(DWORD);
   lResult = RegQueryValueEx(
             hKey,
             szDefaultConnectRegValue[ENABLE_AUTO_CONNECT],
             NULL,
             &dwType,
             (PBYTE)&dwAutostart,
             &dwSize);
      
   if ((lResult != ERROR_SUCCESS)||(dwType != REG_DWORD)) {
      RegCloseKey(hKey);
      return FALSE;
      }

   if (!dwAutostart) {
      return FALSE;
   }

   return TRUE;
}

/*++

--*/


BOOL
CConnectList::QueryDefaultSettings(LPWSTR szClientKey, LPWSTR szConnectKey) {
         
   LONG  lResult;
   HKEY  hKey;
   DWORD dwSize;
   DWORD dwType;

   lResult = RegOpenKeyEx(
             HKEY_LOCAL_MACHINE,
             szRegKeyTaskmanSettings,
             NULL,
             KEY_READ,
             &hKey
             );   
      
   if (lResult != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_ERROR, 
              (TEXT("ERROR: Cannot open \"HKEY_LOCAL_MACHINE\\%s\".\r\n"), szRegKeyTaskmanSettings));
      return FALSE;
      }

             
   dwSize  = sizeof(TCHAR)*MAX_PATH;
   lResult = RegQueryValueEx(
             hKey,
             szDefaultConnectRegValue[CONNECTION_DEFAULT_TYPE],
             NULL,
             &dwType,
             (PBYTE)szClientKey,
             &dwSize);
      
   if ((lResult != ERROR_SUCCESS)||(dwType != REG_SZ)) {
      szClientKey[0] = NULL;
      szConnectKey[0] = NULL;
      RegCloseKey(hKey);
      return FALSE;
      }
         
   dwSize  = sizeof(TCHAR)*MAX_PATH;
   lResult = RegQueryValueEx(
             hKey,
             szDefaultConnectRegValue[CONNECTION_DEFAULT_KEY],
             NULL,
             &dwType,
             (PBYTE)szConnectKey,
             &dwSize);
      
   if ((lResult != ERROR_SUCCESS)||(dwType != REG_SZ)) {
      szConnectKey[0] = NULL;
      RegCloseKey(hKey);
      return FALSE;
      }

   RegCloseKey(hKey);
   return TRUE;

}


/*++

--*/

void
CConnectList::DeleteConnectionFromList(PCONNECT_ENTRY ptr) {
         
   if ((!ptr->prev)&&(!ptr->next)) {
      FreeConnectionData(ptr->cdata);
      delete ptr;
      head = NULL;
      tail = NULL;
      return;
   }

   if (!ptr->prev) {
      ptr->next->prev = NULL;
      head = ptr->next;
      FreeConnectionData(ptr->cdata);
      delete ptr;
      return;
      }

   if (!ptr->next) {
      ptr->prev->next = NULL;
      tail = ptr->prev;
      FreeConnectionData(ptr->cdata);
      delete ptr;
      return;
   }

   ptr->prev->next = ptr->next;
   ptr->next->prev = ptr->prev;
   FreeConnectionData(ptr->cdata);
   delete ptr;
   
}

/*++

--*/

void
CConnectList::DeleteAllConnections() {
   
   PCONNECT_ENTRY ptr;
   PCONNECT_ENTRY next;
   
   ptr = head;
   while (ptr) {
      next = ptr->next;
      DeleteConnectionFromList(ptr);
      ptr = next;
   }
  
}

/*++

--*/

void
CConnectList::InsertConnection(PCONNECT_DATA cdata) {
      
   PCONNECT_ENTRY centry = new CONNECT_ENTRY;   
   if (!centry)
	   return;

   centry->cdata = cdata;
   
   PCONNECT_ENTRY ptr = head;
   
   if (!ptr) {
      centry->next = centry->prev = NULL;
      head = tail = centry;
      return;
   }

   while (1) {
      
      
      if (wcsnicmp(ptr->cdata->lpszDescription,
                   centry->cdata->lpszDescription,
                   wcslen(ptr->cdata->lpszDescription)) >= 0) {
                  
         centry->next = ptr;
         
         if (!ptr->prev) {
            centry->prev = NULL;
            head = centry;
            }
         else {
            centry->prev = ptr->prev;            
            ptr->prev->next = centry;
            }
                  
         ptr->prev = centry;
         return;
         }

      if (!ptr->next) {
         break;
         }

   ptr = ptr->next;
   }
    
   centry->next = NULL;
   centry->prev = tail;
   tail->next = centry;
   tail = centry;
    
   return;

}


/*++

--*/

void
CConnectList::LoadConnections() {
          
   HKEY   hKey;
   LONG   lResult;
   TCHAR  szClientConnectionsKey[MAX_PATH];
   TCHAR  szConnectionKey[MAX_PATH];
   DWORD  cchConnectionKey;
   DWORD  dwIndex;
   PCONNECT_DATA cdata = NULL;
   PCLIENT_DATA  cldata = NULL;

   INT client;
   
   client = GetClientCount();

LOAD_CONNECTIONS_GET_NEXT_CLIENT:
   while (client) {
      client--;
      
      GetClientDataByIndex(client, cldata);
	  if (!cldata)
		  continue;

      StringCchPrintf(szClientConnectionsKey, MAX_PATH, TEXT("%s\\%s\\%s"), szRegKeyRegisteredClients, cldata->lpszRegistrationKey, szRegKeyConnections);  

      lResult = RegOpenKeyEx(
                HKEY_LOCAL_MACHINE,
                szClientConnectionsKey,
                NULL,
                KEY_READ,
                &hKey
                );
      
   
      if (lResult != ERROR_SUCCESS) {
         DEBUGMSG(ZONE_ERROR, 
                 (TEXT("ERROR: Cannot open \"HKEY_LOCAL_MACHINE\\%s\".\r\n"), szClientConnectionsKey));
         goto LOAD_CONNECTIONS_GET_NEXT_CLIENT;
         }
   
      dwIndex = 0;           
      do {
      cchConnectionKey = sizeof (szConnectionKey) / sizeof (szConnectionKey[0]);
      lResult = RegEnumKeyEx(
                hKey,
                dwIndex,
                szConnectionKey,
                &cchConnectionKey,
                NULL,
                NULL,
                NULL,
                NULL);
      
      if (lResult == ERROR_SUCCESS) {
         
         
         cdata = new CONNECT_DATA;
         if (!cdata)
			break;

         memset(cdata, 0, sizeof(CONNECT_DATA));         
         cdata->lpszConnectionKey = (TCHAR*)malloc(GET_WSTR_BYTES(szConnectionKey));         
         if (!cdata->lpszConnectionKey)
		 {
			delete cdata;
			break;
		 }
         wcscpy(cdata->lpszConnectionKey, szConnectionKey);
         
         cdata->lpszClientRegistrationKey = cldata->lpszRegistrationKey;
         cdata->lpszType = cldata->lpszType;
		 cdata->clientClass = cldata->clientClass;

         if (!ReadConnectionFromRegistry(hKey, cdata)) {
            FreeConnectionData(cdata);
         }
         else {
            InsertConnection(cdata);
         }                    
     
      }
        
      dwIndex++;     
   } while (lResult == ERROR_SUCCESS);
   
   RegCloseKey(hKey);   
   }
    
}


/*++

   Note:  RescanConnectionFromRegistry assumes nothing about what the edit helper does to the registry.
          Nothing may have changed, some values may have changed, or the entire regkey may have been
          deleted.

--*/


VOID
CConnectList::RescanConnectionFromRegistry(INT item) {
   
   INT  client;
   HKEY hKey;
   LONG lResult;
   PCONNECT_DATA cdata;
   TCHAR  szConnectionKey[MAX_PATH];
   TCHAR  szClientConnectionsKey[MAX_PATH];
   LPWSTR lpszClientRegistrationKey;
   LPWSTR lpszType;
   CLIENT_CLASS clientClass;

   DEBUGMSG(ZONE_TRACEV,(TEXT("Rescanning connection data for connection %d from registry ...\r\n"), item));

   if (!GetConnectionDataByIndex(item, cdata)) {
      DEBUGMSG(ZONE_ERROR, (TEXT("ERROR: RescanConnectionFromRegistry: GetConnectionDataByIndex failed!\r\n")));  
      return;
      }
  
   // save info required to rescan the key before it is deleted

   wcscpy(szConnectionKey, cdata->lpszConnectionKey);
   lpszClientRegistrationKey = cdata->lpszClientRegistrationKey;
   lpszType = cdata->lpszType;
   clientClass = cdata->clientClass;
   
   DeleteConnectionFromList(item);
      
   // verify the connections key for the client is still around

   StringCchPrintf(szClientConnectionsKey, MAX_PATH, TEXT("%s\\%s\\Connections"), szRegKeyRegisteredClients, lpszClientRegistrationKey);
   DEBUGMSG(ZONE_TRACEV,(TEXT("RescanConnectionFromRegistry: verifying \"%s\" regkey exists...\r\n"), szClientConnectionsKey)); 
   
   lResult = RegOpenKeyEx(
             HKEY_LOCAL_MACHINE,
             szClientConnectionsKey,
             NULL,
             KEY_READ,
             &hKey
             );

   if (lResult != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_TRACEV,(TEXT("  key does not exist.\r\n"), szClientConnectionsKey));
      EnumConnections();
      return;
      }

   DEBUGMSG(ZONE_TRACEV,(TEXT("  key exists.\r\n")));   

   // Assume the connection key is still around

   cdata = new CONNECT_DATA;
   if (!cdata)
   {
	   RegCloseKey(hKey);
	   return;
   }

   memset(cdata, 0, sizeof(CONNECT_DATA));         
   cdata->lpszConnectionKey = (TCHAR*)malloc(GET_WSTR_BYTES(szConnectionKey));
   ASSERT(cdata->lpszConnectionKey);
   wcscpy(cdata->lpszConnectionKey, szConnectionKey);   
   cdata->lpszClientRegistrationKey = lpszClientRegistrationKey;
   cdata->lpszType = lpszType;
   cdata->clientClass = clientClass;
   

   if (ReadConnectionFromRegistry(hKey, cdata)) {
        InsertConnection(cdata);
        EnumConnections();
        RegCloseKey(hKey);
        return;
      }

   DEBUGMSG(ZONE_TRACEV,(TEXT("The connection key \"%s\" no longer exists.\r\n"), cdata->lpszConnectionKey)); 
   FreeConnectionData(cdata);

   // the connection key was either changed or deleted. Look for new client connection key ...
   
   client = GetClientIndexByRegKey(lpszClientRegistrationKey);
   FindConnectionNew(client);
   EnumConnections();
   RegCloseKey(hKey);
   return;
}


/*++

--*/


BOOL
CConnectList::ReadConnectionFromRegistry(HKEY hKey, PCONNECT_DATA cdata) {
  
   HKEY  hKey2;
   LONG  lResult;
   
   lResult = 
      RegOpenKeyEx(
         hKey,
         cdata->lpszConnectionKey,
         0,
         KEY_READ,
         &hKey2
         );
   
   if (lResult != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_ERROR,
              (TEXT("ERROR: Cannot open regkey for connection \"%s\".\r\n"),
               cdata->lpszConnectionKey));
      return FALSE;
      }
  
   
   if(!ReadConnectionValues(hKey2, cdata)) {
      DEBUGMSG(ZONE_ERROR,
              (TEXT("ERROR: Invalid connection data for \"%s\".\r\n"),
               cdata->lpszConnectionKey));
      RegCloseKey(hKey2);      
      return FALSE;
   }
      
   RegCloseKey(hKey2);
   return TRUE;
}


/*++

--*/


VOID 
CConnectList::ValidateConnectionPID(PCONNECT_DATA pcdata, DWORD dwProcessId) {

   HANDLE hProcess;
   DWORD dwExitCode;
   BOOL bResult;

   DEBUGMSG(ZONE_TRACE,(TEXT(" TASKMAN: Attepmpting to validate connection %s against PID = 0x%08x.\r\n"), pcdata->lpszDescription, dwProcessId));
   hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);

   if (!hProcess) {
      goto EXIT_VALIDATE_CONNECTION_PID;
      }

   bResult = GetExitCodeProcess(hProcess, &dwExitCode);

   if ((!bResult)||(dwExitCode != STILL_ACTIVE)) {
      goto EXIT_VALIDATE_CONNECTION_PID;
      }


   DEBUGMSG(ZONE_TRACE,(TEXT("     Connection is active.\r\n")));
   pcdata->pi.dwProcessId = dwProcessId;
   pcdata->pi.hProcess = (HANDLE)dwProcessId;

EXIT_VALIDATE_CONNECTION_PID:

   CloseHandle(hProcess);
   return;
}

/*++

--*/

BOOL
CConnectList::ReadConnectionValues(HKEY hKey, PCONNECT_DATA cdata) {
   
   INT     index;
   DWORD   dwType;
   DWORD   dwSize;
   DWORD   dwProcessId;
   LONG    lResult;
   TCHAR   szTemp[MAX_PATH];
   TCHAR*  lpszTempArray[NUM_CONNECT_REG_SZ_VALUES];
   int          NameLength = GET_WSTR_BYTES(cdata->lpszConnectionKey) + 4; //for space and quotes    
   
   for (index = 0; index < NUM_CONNECT_REG_SZ_VALUES; index++) {      
      dwSize  = sizeof(szTemp);
      lResult = 
         RegQueryValueEx(
               hKey,
               szConnectionRegValue[index],
               NULL,
               &dwType,
               (PBYTE)szTemp,
               &dwSize
               );

	  if ( index == CONNECT_REG_DWORD_HIDE_CONNECTION && lResult == ERROR_SUCCESS)
	  {
		  goto Done;
	  }

      if ((lResult != ERROR_SUCCESS)||(dwType != REG_SZ)) 
        {
         lpszTempArray[index] = NULL;
         continue;
        } 


      int   ValueLength = GET_WSTR_BYTES(szTemp) + 2;
      int   StringLength = ValueLength;
      TCHAR*  pStringToAppend = NULL;

	  if ( 0 == _tcscmp(cdata->lpszType,TEXT("RDP") ))
	  {
		  if ( (index == CONNECT_REG_SZ_START_CMDLINE) ||
				(index == CONNECT_REG_SZ_PROP_CMDLINE)  || 
				(index == CONNECT_REG_SZ_DELETE_CMDLINE) )
				{
				pStringToAppend = cdata->lpszConnectionKey;
				StringLength = NameLength + ValueLength;
				}
	  }      
       lpszTempArray[index] = (TCHAR*) malloc (StringLength);

        if(!lpszTempArray[index])
            {
            DEBUGMSG(ZONE_ERROR, (L"Error allocating memory"));
            goto Done;
            }
        
        HRESULT     hr = E_FAIL;

        if(pStringToAppend)
            {
            hr = StringCchPrintf(lpszTempArray[index], StringLength, L"%s\"%s\"", szTemp, pStringToAppend);
            }
        else
            {
            hr = StringCchPrintf(lpszTempArray[index], StringLength, L"%s", szTemp);
            }

        if(!SUCCEEDED(hr))
            {
            DEBUGMSG(ZONE_ERROR, (L"Error in StringCchPrintf; HResult %d", hr));
            goto Done;
            }
   }
   
   // validate the connection's entries:

   if ( (!lpszTempArray[CONNECT_REG_SZ_DESCRIPTION]) || (!wcslen(lpszTempArray[CONNECT_REG_SZ_DESCRIPTION])) ) {
      DEBUGMSG(ZONE_ERROR,
              (TEXT("ERROR: No \"%s\" value specified for connection \"%s\".\r\n"),
               szConnectionRegValue[CONNECT_REG_SZ_DESCRIPTION],
               lpszTempArray[CONNECT_REG_SZ_DESCRIPTION]));

      goto Done;
      }
   
   if ((!lpszTempArray[CONNECT_REG_SZ_STARTEXE]) || (!wcslen(lpszTempArray[CONNECT_REG_SZ_STARTEXE])) ){
      DEBUGMSG(ZONE_ERROR,
              (TEXT("ERROR: No \"%s\" value specified for connection \"%s\".\r\n"),
               szConnectionRegValue[CONNECT_REG_SZ_STARTEXE],
               lpszTempArray[CONNECT_REG_SZ_STARTEXE]));

      goto Done;
      }


   //To support a default RDP connection entry, it is legitimate to have all the remaining fields be NULL
   //If no command line is specified, cetsc launches the UI and IE goes to its home page
   //If lpszPropertiesExe and lpszDeleteExe are not specified, editing the properties or deleting the connection entry is not allowed

   //If a properties exe is specified, a command line has to be specified
   if ( ((lpszTempArray[CONNECT_REG_SZ_PROP_EXE]) && (lpszTempArray[CONNECT_REG_SZ_PROP_EXE][0] != _T('\0'))) &&
	    ((!lpszTempArray[CONNECT_REG_SZ_PROP_CMDLINE]) || (lpszTempArray[CONNECT_REG_SZ_PROP_CMDLINE][0] == _T('\0'))) )
   {
		DEBUGMSG(ZONE_ERROR, 
              (TEXT("ERROR: No \"%s\" value specified for connection \"%s\".\r\n"),
               szConnectionRegValue[CONNECT_REG_SZ_PROP_CMDLINE],
               lpszTempArray[CONNECT_REG_SZ_PROP_CMDLINE]));

      goto Done;
   }

   //If a delete exe is specified, a command line has to be specified
   if ( ((lpszTempArray[CONNECT_REG_SZ_DELETE_EXE]) && (lpszTempArray[CONNECT_REG_SZ_DELETE_EXE][0] != _T('\0'))) &&
	    ((!lpszTempArray[CONNECT_REG_SZ_DELETE_CMDLINE]) || (lpszTempArray[CONNECT_REG_SZ_DELETE_CMDLINE][0] == _T('\0'))) )
   {
      DEBUGMSG(ZONE_ERROR,
              (TEXT("ERROR: No \"%s\" value specified for connection \"%s\".\r\n"),
               szConnectionRegValue[CONNECT_REG_SZ_DELETE_CMDLINE],
               lpszTempArray[CONNECT_REG_SZ_DELETE_CMDLINE]));

      goto Done;
   }

   cdata->lpszDescription = lpszTempArray[CONNECT_REG_SZ_DESCRIPTION];
   cdata->lpszStartExe = lpszTempArray[CONNECT_REG_SZ_STARTEXE];
   cdata->lpszStartCmdLineArgs = lpszTempArray[CONNECT_REG_SZ_START_CMDLINE];
   cdata->lpszPropertiesExe = lpszTempArray[CONNECT_REG_SZ_PROP_EXE];
   cdata->lpszPropertiesCmdLineArgs = lpszTempArray[CONNECT_REG_SZ_PROP_CMDLINE];
   cdata->lpszDeleteExe = lpszTempArray[CONNECT_REG_SZ_DELETE_EXE];
   cdata->lpszDeleteCmdLineArgs = lpszTempArray[CONNECT_REG_SZ_DELETE_CMDLINE];


   // deal with the possibility that this may be a restart

   if (bRestart) {
      dwSize  = sizeof(dwProcessId);
      lResult = 
         RegQueryValueEx(
            hKey,
            szRegValLastPID,
            NULL,
            &dwType,
            (PBYTE)&dwProcessId,
            &dwSize
            );
      
      if ((lResult == ERROR_SUCCESS)&&(dwType == REG_DWORD)) {
         ValidateConnectionPID(cdata, dwProcessId);
         }  
   }
   return TRUE;

Done:
   for (index = 0; index < NUM_CONNECT_REG_SZ_VALUES; index++) {      
         free(lpszTempArray[index]);
   }
	return FALSE;
}

/*++

--*/


BOOL
CConnectList::FindConnectionNew(INT client) {
     
   DWORD  dwIndex, dwConnectionIndex;
   TCHAR  szClientConnectionsKey[MAX_PATH];   
   TCHAR  szConnectionKey[MAX_PATH];
   DWORD  cchConnectionKey;   
   HKEY   hKey;
   LONG   lResult;
   PCONNECT_DATA cdata = NULL;
   PCLIENT_DATA  cldata = NULL;

   GetClientDataByIndex(client, cldata);
   if (!cldata)
	   return FALSE;
   StringCchPrintf(szClientConnectionsKey, MAX_PATH, TEXT("%s\\%s\\Connections"), szRegKeyRegisteredClients, cldata->lpszRegistrationKey);
      
   lResult = RegOpenKeyEx(
             HKEY_LOCAL_MACHINE,
             szClientConnectionsKey,
             NULL,
             KEY_READ,
             &hKey
             );

      if (lResult != ERROR_SUCCESS) {
         DEBUGMSG(ZONE_ERROR, 
                 (TEXT("ERROR: Cannot open \"HKEY_LOCAL_MACHINE\\%s\".\r\n"), szClientConnectionsKey));
      return FALSE;
      }

   dwIndex = 0;
   do {
   cchConnectionKey = sizeof (szConnectionKey) / sizeof (szConnectionKey[0]);
   lResult = RegEnumKeyEx(
             hKey,
             dwIndex,
             szConnectionKey,
             &cchConnectionKey,
             NULL,
             NULL,
             NULL,
             NULL);
         
   if (lResult == ERROR_SUCCESS) {   
      DEBUGMSG(ZONE_TRACEV,(TEXT("Found connection \"%s\".\r\n"), szConnectionKey));
      
      dwConnectionIndex = GetConnectionIndexByConnKeyAndClientKey(szConnectionKey, cldata->lpszRegistrationKey);
      
      if (dwConnectionIndex == -1) {
        
         cdata = new CONNECT_DATA;
         if (!cdata)
			 break;
         memset(cdata, 0, sizeof(CONNECT_DATA));         
         cdata->lpszConnectionKey = (TCHAR*)malloc(GET_WSTR_BYTES(szConnectionKey));         
         if (!cdata->lpszConnectionKey)
		 {
			delete cdata;
			break;
		 }
         wcscpy(cdata->lpszConnectionKey, szConnectionKey);
                
         cdata->lpszClientRegistrationKey = cldata->lpszRegistrationKey;
         cdata->lpszType = cldata->lpszType;

         if (!ReadConnectionFromRegistry(hKey, cdata)) {
            FreeConnectionData(cdata);
         }
         else {
            DEBUGMSG(ZONE_TRACE,(TEXT("Connection \"%s\" being inserted.\r\n"), szConnectionKey));
            InsertConnection(cdata);
            RegCloseKey(hKey);
            return TRUE;
         }      
      
      
      }
    
   }
      dwIndex++;     
   } while (lResult == ERROR_SUCCESS);
  
   RegCloseKey(hKey);
     
   return FALSE;   
}

/*++

--*/



void
CConnectList::FreeConnectionData(PCONNECT_DATA cdata){

   // Note: Don't free lpszClientRegistrationKey and lpszType because
   //       these are pointers to memory allocated for the client list!

   if (cdata) {
      DEBUGMSG(ZONE_TRACE,(TEXT("Deleteing connection data for %s\r\n"), cdata->lpszDescription));
      if (cdata->lpszConnectionKey) free(cdata->lpszConnectionKey);
      if (cdata->lpszDescription) free(cdata->lpszDescription);
      if (cdata->lpszStartExe) free(cdata->lpszStartExe);
      if (cdata->lpszStartCmdLineArgs) free(cdata->lpszStartCmdLineArgs);
      if (cdata->lpszPropertiesExe) free(cdata->lpszPropertiesExe);
      if (cdata->lpszPropertiesCmdLineArgs) free(cdata->lpszPropertiesCmdLineArgs);
      if (cdata->lpszDeleteExe) free(cdata->lpszDeleteExe);
      if (cdata->lpszDeleteCmdLineArgs) free(cdata->lpszDeleteCmdLineArgs);
      delete cdata;
   }
   cdata = NULL;

}


/*++

--*/

#ifdef DEBUG
void
CConnectList::DbgDumpConnections() {
   
   PCONNECT_ENTRY ptr;
   INT count = 0;
   DEBUGMSG(ZONE_INFO,(TEXT(" ----- Dumping Connections List -----\r\n")));
   
   ptr = head;
   while (ptr) {
      DEBUGMSG(ZONE_INFO,(TEXT("ConnectionKey: %s\r\n"), ptr->cdata->lpszConnectionKey));      
      DEBUGMSG(ZONE_INFO,(TEXT("  ClientRegKey: %s\r\n"), ptr->cdata->lpszClientRegistrationKey));
      DEBUGMSG(ZONE_INFO,(TEXT("  Type: %s\r\n"), ptr->cdata->lpszType));
      DEBUGMSG(ZONE_INFO,(TEXT("  Description: %s\r\n"), ptr->cdata->lpszDescription));
      DEBUGMSG(ZONE_INFO,(TEXT("  StartExe: %s\r\n"), ptr->cdata->lpszStartExe));
      DEBUGMSG(ZONE_INFO,(TEXT("  StartCmdLineArgs: %s\r\n"), ptr->cdata->lpszStartCmdLineArgs));
      DEBUGMSG(ZONE_INFO,(TEXT("  PropertiesExe: %s\r\n"), ptr->cdata->lpszPropertiesExe));
      DEBUGMSG(ZONE_INFO,(TEXT("  PropertiesCmdLineArgs: %s\r\n"), ptr->cdata->lpszPropertiesCmdLineArgs));
      DEBUGMSG(ZONE_INFO,(TEXT("  DeleteExe: %s\r\n"), ptr->cdata->lpszDeleteExe));
      DEBUGMSG(ZONE_INFO,(TEXT("  DeleteCmdLineArgs: %s\r\n"), ptr->cdata->lpszDeleteCmdLineArgs));
      ptr = ptr->next;
      count++;
   }

   DEBUGMSG(ZONE_INFO,(TEXT(" ----------- End of list -----------\r\n")));
   DEBUGMSG(ZONE_INFO,(TEXT("%d connections(s) in list.\r\n"), count));

}

#endif



